package com.gitee.huanminabc.jnetty.utils;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import com.gitee.huanminabc.jnetty.base.ByteBufUtil;
import com.gitee.huanminabc.jnetty.base.NettyBizException;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author huanmin
 * @date 2023/12/1
 */
public class NettyHttpUtil {
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String CONTENT_LENGTH = "Content-Length";
    public static final String CONNECTION = "Connection";
    public static final String KEEP_ALIVE = "keep-alive";

    public static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
    public static final String JSON_CONTENT_TYPE = "application/json";

    public static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
    public static final String HTML_CONTENT_TYPE = "text/html";
    //文本类型
    public static final String TEXT_CONTENT_TYPE = "text/plain";

    public static final String JSON_CONTENT_TYPE_UTF_8 = "application/json;charset=utf-8";
    public static final String HTML_CONTENT_TYPE_UTF_8 = "text/html;charset=utf-8";


    /**
     * 获取post请求的内容
     *
     * @param request
     * @return JSONObject或者Map<String, Object>
     */
    public static JSONObject getRequestPostBody(FullHttpRequest request) {
        // 获取请求的 Content-Type 类型
        String contentType = request.headers().get(CONTENT_TYPE);
        //如果开头是multipart/form-data; boundary=--------------------------813852585178433212458023 那么设置为multipart/form-data
        if (contentType.startsWith(MULTIPART_CONTENT_TYPE)) {
            contentType = MULTIPART_CONTENT_TYPE;
        }
        // 获取报文信息
        switch (contentType) {
            case FORM_CONTENT_TYPE:
                JSONObject json = null;
                try {
                    String jsonFromStr = ByteBufUtil.byteBufToStr(request.content());
                    json = new JSONObject();
                    // 最常见的 POST 提交数据的方式了。浏览器的原生 表单
                    String[] keyvalues = jsonFromStr.split("&");
                    for (String keyvalue : keyvalues) {
                        String[] kv = keyvalue.split("=");
                        if (kv.length == 2) {
                            String key = kv[0];
                            String value = kv[1];
                            // 解决中文乱码问题
                            json.put(key, URLDecoder.decode(value, StandardCharsets.UTF_8.name()));
                        }
                    }
                } catch (UnsupportedEncodingException e) {
                    throw new NettyBizException("不支持的编码格式", e);
                } catch (Exception e) {
                    throw new NettyBizException("解析请求参数失败", e);
                }
                return json;
            case JSON_CONTENT_TYPE:
                String jsonDataStr = ByteBufUtil.byteBufToStr(request.content());
                if (JSON.isValid(jsonDataStr)) {
                    if (JSON.isValidObject(jsonDataStr)) {
                        // 如果是一个 JSON 对象
                        return JSON.parseObject(jsonDataStr);
                    }else{
                        // 如果是一个 JSON 数组
                        JSONArray jsonArray = JSON.parseArray(jsonDataStr);
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("$args1$", jsonArray);
                        return jsonObject;
                    }
                }else{
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("$args1$", jsonDataStr);
                    return  jsonObject;
                }
            case MULTIPART_CONTENT_TYPE:
                // 常见的 POST 数据提交的方式。我们使用表单上传文件时，必须让 表单的 enctype 等于 multipart/form-data。
                return getRequestFormData(request);
            default:
                throw new NettyBizException("不支持的请求类型,目前还解析不了这种协议{}的请求 ,目前支持的协议:{}", contentType, Arrays.asList(FORM_CONTENT_TYPE, JSON_CONTENT_TYPE, MULTIPART_CONTENT_TYPE));
        }

    }

    /**
     * 获取url参数 , 例如：http://localhost:8080?name=netty&age=12
     *
     * @param request
     * @return
     */
    public static JSONObject getRequestUrlParams(FullHttpRequest request) {
        QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
        Map<String, List<String>> parameters = decoder.parameters();
        JSONObject jsonObject = new JSONObject();
        Iterator<Map.Entry<String, List<String>>> iterator = parameters.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<String>> next = iterator.next();
            List<String> value = next.getValue();
            jsonObject.put(next.getKey(), value.size() > 1 ? value : next.getValue().get(0));
        }
        return jsonObject;
    }


    /**
     * 获取表单参数 , 就是http表单中的input标签的name和value
     *
     * @param request
     * @return
     */
    public static JSONObject getRequestFormData(FullHttpRequest request) {
        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
        List<InterfaceHttpData> httpPostData = decoder.getBodyHttpDatas();
        JSONObject params = new JSONObject();
        for (InterfaceHttpData data : httpPostData) {
            //获取普通参数
            if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                MemoryAttribute attribute = (MemoryAttribute) data;
                params.put(attribute.getName(), attribute.getValue());
            }
            //获取文件
            if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
                FileUpload fileUpload = (FileUpload) data;
                List<FileUpload> files = (List<FileUpload>) params.getOrDefault("$fileList$", new ArrayList<>());
                files.add(fileUpload);
                params.put("$fileList$", files);
            }
        }
        return params;
    }

    //只获取上传的所有文件
    public static List<FileUpload> getFormRequestFiles(FullHttpRequest request) throws IOException {
        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
        List<InterfaceHttpData> httpPostData = decoder.getBodyHttpDatas();
        List<FileUpload> fileUploadList = new ArrayList<>();
        for (InterfaceHttpData data : httpPostData) {
            //获取文件
            if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {

                FileUpload fileUpload = (FileUpload) data;
                fileUploadList.add(fileUpload);
            }
        }
        return fileUploadList;
    }


    /**
     * 响应HTTP的请求,文本返回值处理, 一旦调用就会关闭连接
     *
     * @param ctx
     * @param contentType 返回类型
     * @param str         返回值 ,   注意:如果是文本需要在结尾加上"\r\n"
     */
    public static void Response(ChannelHandlerContext ctx, String contentType, ByteBuf str, Map<String, Object> header) {
        Response(ctx, contentType, str, header, HttpResponseStatus.OK);
    }
    public static void Response(ChannelHandlerContext ctx, String contentType, ByteBuf str, Map<String, Object> header,HttpResponseStatus httpResponseStatus) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, str);
        response.headers().set(CONTENT_TYPE, contentType);
        response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
        response.headers().set(CONNECTION, KEEP_ALIVE);
        if (header != null) {
            header.forEach((k, v) -> response.headers().set(k, v));
        }
        ctx.writeAndFlush(response);
        ctx.close();
    }

    /**
     * 文件下载
     *
     * @param file
     * @param channelHandlerContext
     */
    public static void responseFile(File file, ChannelHandlerContext channelHandlerContext, Map<String, Object> header) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().add(CONTENT_TYPE, "application/octet-stream");
        response.headers().add(CONTENT_LENGTH, file.length());
        //文件名称
        String fileName = file.getName();
        //设置文件名
        response.headers().add("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));
        if (header != null) {
            header.forEach((k, v) -> response.headers().set(k, v));
        }
        Channel channel = channelHandlerContext.channel();
        FileInputStream ips = null;
        try {
            ips = new FileInputStream(file);
            byte[] by = new byte[1024];//每次读取1024个字节
            int read = -1;
            while ((read = ips.read(by, 0, by.length)) != -1) {
                response.content().writeBytes(by, 0, read);
            }
            channel.writeAndFlush(response);
        } catch (FileNotFoundException e) {
            throw new NettyBizException("文件未找到: " + file.getAbsolutePath(), e);
        } catch (IOException e) {
            throw new NettyBizException("读取文件失败", e);
        } finally {
            if (ips != null) {
                try {
                    ips.close();
                } catch (IOException ignore) {}
            }
        }
    }
    public static void responseInputStream(InputStream inputStream, String fileName, ChannelHandlerContext channelHandlerContext, Map<String, Object> header) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().add(CONTENT_TYPE, "application/octet-stream; charset=UTF-8");
        response.headers().add("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));
        if (header != null) {
            header.forEach((k, v) -> response.headers().set(k, v));
        }
        Channel channel = channelHandlerContext.channel();
        try {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                response.content().writeBytes(buffer, 0, bytesRead);
            }
            response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
            channel.writeAndFlush(response);
        } catch (IOException e) {
            throw new NettyBizException("读取输入流失败", e);
        } finally {
            try {
                inputStream.close();
            } catch (IOException ignore) {}

        }
    }

    //字节模式的文件下载
    public static void responseByte(byte[] file, String fileName, ChannelHandlerContext channelHandlerContext, Map<String, Object> header) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().add(CONTENT_TYPE, "application/octet-stream; charset=UTF-8");
        response.headers().add(CONTENT_LENGTH, file.length);
        response.headers().add("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));
        if (header != null) {
            header.forEach((k, v) -> response.headers().set(k, v));
        }

        Channel channel = channelHandlerContext.channel();
        response.content().writeBytes(file);
        channel.writeAndFlush(response);
    }

    //返回HTMl

    /**
     * @param ctx
     * @param html <h1>你好</h1>
     */
    public static void ResponseHtml(ChannelHandlerContext ctx, String html) {
        String msg = "<html> \t<meta charset=\"UTF-8\" />" + html + "</html>";
        ByteBuf byteBuf = ByteBufUtil.createByteBuf(msg);
        Response(ctx, HTML_CONTENT_TYPE_UTF_8, byteBuf, null);
    }

}
